﻿using LightCAD.MathLib;
using LightCAD.Three;
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Net;
using System.Reactive.Joins;
using System.Xml.Schema;
using GeometryData = LightCAD.Model.GeometryData;

namespace QdStruct
{
    public class BeamAction : ComponentInstance2dAction
    {
        private PointInputer PointInputer { get; set; }
        private QdBeamInstance _beamInstance = null;
        private Vector2 StartPoint;
        private Vector2 CenterPoint;
        public BeamAction() { }
        public BeamAction(IDocumentEditor docEditor) : base(docEditor) 
        {
            this.commandCtrl.WriteInfo("命令：Beam");
        }
        public async void ExecCreate(string[] args = null)
        {
            this.StartCreating();
            this.PointInputer = new PointInputer(this.docEditor);
            _beamInstance = new QdBeamInstance(ComponentManager.GetCptDef<QdBeamDef>("结构梁", "结构梁", BeamAttribute.BuiltinUuid));
            _beamInstance.Width = 200;
            _beamInstance.Height = 400;
            _beamInstance.Bottom = 600;
            var result = await this.PointInputer.Execute("请选择结构梁起点或[弧梁(A)/放弃(U)]:");
            if (result == null || this.PointInputer.isCancelled)
            {
                this.Cancel();
                goto End;
            }
            if(result.ValueX != null  && result.ValueX is Vector2)
            {
                StartPoint = result.ValueX as Vector2;
                var result2 = await this.PointInputer.Execute("请选择结构梁终点:");
                if (result2 == null || result2.ValueX == null || !(result2.ValueX is Vector2) || this.PointInputer.isCancelled)
                {
                    this.Cancel();
                    goto End;
                }
                var endPoint = result2.ValueX as Vector2;
                _beamInstance.Lines = new Curve2d[] { new Line2d(StartPoint.Clone(), endPoint.Clone()) };
            }
            else
            {
                if (result.Option!=null && result.Option.Trim().ToUpper() =="A")
                {
                    var result2 = await this.PointInputer.Execute("请选择弧梁圆心:");
                    if (result2 == null || result2.ValueX == null || !(result2.ValueX is Vector2) || this.PointInputer.isCancelled)
                    {
                        this.Cancel();
                        goto End;
                    }
                    CenterPoint = result2.ValueX as Vector2;
                    var arc = new Arc2d();
                    arc.Center = CenterPoint.Clone();
                    var result3 = await this.PointInputer.Execute("请选择弧形结构梁起点:");
                    if (result3 == null || result3.ValueX == null || !(result3.ValueX is Vector2) || this.PointInputer.isCancelled)
                    {
                        this.Cancel();
                        goto End;
                    }
                    StartPoint = result3.ValueX as Vector2;
                    arc.Radius = StartPoint.DistanceTo(CenterPoint);
                    arc.StartAngle = StartPoint.Clone().Sub(CenterPoint).Angle();
                    _beamInstance.Lines = new Curve2d[] { arc };
                Change:
                    var result4 = await this.PointInputer.Execute("请选择结构梁终点或[切换方向(C)/放弃(U)]:");
                    if (result4 == null || this.PointInputer.isCancelled)
                    {
                        this.Cancel();
                        goto End;
                    }
                    if (result4.ValueX != null && result4.ValueX is Vector2)
                    {
                        var endPoint = result4.ValueX as Vector2;
                        arc.EndAngle = endPoint.Clone().Sub(CenterPoint).Angle();
                    }
                    else
                    {
                        if (result4.Option!=null && result4.Option.Trim().ToUpper()=="C")
                        {
                            arc.IsClockwise = !arc.IsClockwise;
                            goto Change;
                        }
                        goto End;
                    }

                }
                else
                {
                    goto End;
                }
            }
            this.DrawBeam(_beamInstance);

        End:
            this.PointInputer = null;
            this.EndCreating();
            this._beamInstance = null;
            this.StartPoint = null;
            this.CenterPoint = null;
        }
     
        public override void Draw(SKCanvas canvas, LcElement element, Vector2 offset)
        {
            var beamInstance = element as QdBeamInstance;
            var curves = beamInstance.Curves?.FirstOrDefault().Curve2ds;
            var pen = this.GetDrawPen(element);
            if (pen == LightCAD.Runtime.Constants.defaultPen)
            {
                pen = new SKPaint { Color = SKColors.White, IsStroke = true };
            }
            DrawBeamCurves(canvas, curves, pen);
        }
        public override void Draw(SKCanvas canvas, LcElement element, Matrix3 matrix)
        {
            var beamInstance = element as QdBeamInstance;
            // var curves = beamInstance.ShapeCurves;
            var curves = beamInstance.Curves?.FirstOrDefault().Curve2ds;
            var pen = this.GetDrawPen(element);
            if (pen == LightCAD.Runtime.Constants.defaultPen)
            {
                pen = new SKPaint { Color = SKColors.White, IsStroke = true };
            }
            DrawBeamCurves(canvas,curves,pen);
        }
        public void DrawBeamCurves(SKCanvas canvas, List<Curve2d> curves, SKPaint pen)
        {
            foreach (var curve in curves)
            {
                switch (curve.Type)
                {
                    case Curve2dType.Arc2d:
                        {
                            var arc = curve as Arc2d;
                            Matrix3 matrix = Matrix3.GetTranslate(new Vector2(0, 0));
                            this.vportRt.DrawArc(arc, matrix,canvas, pen);
                            break;
                        }
                    case Curve2dType.Line2d:
                        {
                            var line = curve as Line2d;
                            Matrix3 matrix = Matrix3.GetTranslate(new Vector2(0, 0));
                            var start = this.vportRt.ConvertWcsToScr(line.Start).ToSKPoint();
                            var end = this.vportRt.ConvertWcsToScr(line.End).ToSKPoint();
                            canvas.DrawLine(start,end,pen);
                            break;
                        }
                    default:
                        break;
                }
            }

        }
         private void DrawBeam(QdBeamInstance beamInstance) 
        {
            if (beamInstance == null)
                return;
            beamInstance.Initilize(this.docRt.Document);
            this.docRt.Document.ModelSpace.InsertElement(beamInstance);
        }
        public override void Cancel()
        {
            base.Cancel();
            this.vportRt.SetCreateDrawer(null);
        }
        public override void DrawAuxLines(SKCanvas canvas)
        {
            if (_beamInstance == null || _beamInstance.Lines == null)
            {
                return;
            }
            var mp = this.vportRt.PointerMovedPosition.ToVector2d();
            var wcs_mp = this.vportRt.ConvertScrToWcs(mp);
            if (_beamInstance.Lines[0] is Line2d line)
            {
                line.End = wcs_mp.Clone();
                _beamInstance.Lines[0] = line;
            }
            else if (_beamInstance.Lines[0] is Arc2d arc)
            {
                arc.EndAngle = wcs_mp.Clone().Sub(CenterPoint).Angle();
                _beamInstance.Lines[0] = arc;
            }
            _beamInstance.ResetCache();
            using (var elePen = new SKPaint { Color = SKColors.White, IsStroke = true })
            {
                DrawBeamCurves(canvas, _beamInstance.Curves.FirstOrDefault().Curve2ds, elePen);
            }
        }
        #region Grip
        private string _gripName;
        private Vector2 _position;

        public override ControlGrip[] GetControlGrips(LcElement element)
        {
            var beamInstance = element as QdBeamInstance;
            var grips = new List<ControlGrip>();
            if (beamInstance.Lines[0] is Line2d line)
            {
                var startPoint = new ControlGrip
                {
                    Element = beamInstance,
                    Name = "StartPoint",
                    Position = line.Start.Clone()
                };
                grips.Add(startPoint);
                var endPoint = new ControlGrip
                {
                    Element = beamInstance,
                    Name = "EndPoint",
                    Position = line.End.Clone()
                };
                grips.Add(endPoint);
                var centerPoint = new ControlGrip
                {
                    Element = beamInstance,
                    Name = "CenterPoint",
                    Position = line.GetPoints(2)[1]
                };
                grips.Add(centerPoint);

            }
            else if (beamInstance.Lines[0] is Arc2d arc)
            {
                var points = arc.GetPoints(1);
                var startPoint = new ControlGrip
                {
                    Element = beamInstance,
                    Name = "StartPoint",
                    Position = points.First()
                };
                grips.Add(startPoint);
                var endPoint = new ControlGrip
                {
                    Element = beamInstance,
                    Name = "EndPoint",
                    Position = points.Last()
                };
                grips.Add(endPoint);
                var centerPoint = new ControlGrip
                {
                    Element = beamInstance,
                    Name = "CenterPoint",
                    Position = arc.Center.Clone()
                };
                grips.Add(centerPoint);
            }
            return grips.ToArray();
        }

        public override void SetDragGrip(LcElement element, string gripName, Vector2 position, bool isEnd)
        {
            var beamInstance = element as QdBeamInstance;
            _beamInstance = beamInstance;
            if (!isEnd)
            {
                _gripName = gripName;
                _position = position;
                beamInstance.OnPropertyChangedAfter("Lines", null, null);
                if (gripName == "CenterPoint")
                {
                    if (beamInstance.Lines[0] is Line2d line)
                    {
                        position = position.Sub(line.GetPoints(2)[1]);
                    }
                    else if (beamInstance.Lines[0] is Arc2d arc) {
                        position = position.Sub(arc.Center);
                    }
                    beamInstance.Lines[0].Translate(position);
                    beamInstance.ResetCache();
                }
                if (gripName == "StartPoint")
                {
                    if (beamInstance.Lines[0] is Line2d line)
                    {
                        var offset = position.Clone().Sub(line.Start);
                        line.Start.Add(offset);
                        beamInstance.ResetCache();
                    }
                    else if (beamInstance.Lines[0] is Arc2d arc)
                    {
                        var sp = position.Sub(arc.Center).Angle();
                        arc.StartAngle = sp;
                        beamInstance.ResetCache();
                    }
                }
                if (gripName == "EndPoint")
                {
                    if (beamInstance.Lines[0] is Line2d line)
                    {
                        line.End = position.Clone();
                        beamInstance.ResetCache();
                    }
                    else if (beamInstance.Lines[0] is Arc2d arc)
                    {
                        var ep = position.Clone().Sub(arc.Center).Angle();
                        arc.EndAngle = ep;
                        beamInstance.ResetCache();
                    }
                }
                beamInstance.OnPropertyChangedAfter("Lines", null, null);
            }
            else
            {
            }

        }
        public override List<PropertyObserver> GetPropertyObservers()
        {
            //return base.GetPropertyObservers();
            var list = new List<PropertyObserver>() {
               new PropertyObserver()
            {
                Name = "Width",
                DisplayName = "宽度",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as QdBeamInstance).Parameters.GetValue<double>("Width"),
                Setter = (ele, value) =>
                {
                    var beam = (ele as QdBeamInstance);
                    if (!double.TryParse(value.ToString(),out var width)||width<0)
                        return;
                    beam.OnPropertyChangedBefore("Width",beam.Parameters.GetValue<double>("Width"),width);
                    beam.Parameters.SetValue("Width",width );
                    beam.ResetCache();
                    beam.OnPropertyChangedAfter("Width",beam.Parameters.GetValue<double>("Width"),width);
                }
            },  new PropertyObserver()
            {
                Name = "Height",
                DisplayName = "高度",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as QdBeamInstance).Parameters.GetValue<double>("Height"),
                Setter = (ele, value) =>
                {
                    var beam = (ele as QdBeamInstance);
                    if (!double.TryParse(value.ToString(),out var height)||height<0)
                        return;
                    beam.OnPropertyChangedBefore("Height",beam.Parameters.GetValue<double>("Height"),height);
                    beam.Parameters.SetValue("Height",height );
                    beam.ResetCache();
                    beam.OnPropertyChangedAfter("Height",beam.Parameters.GetValue<double>("Height"),height);
                }
            },  new PropertyObserver()
            {
                Name = "Bottom",
                DisplayName = "底高度",
                CategoryName = "Geometry",
                CategoryDisplayName = "几何图形",
                PropType=PropertyType.Double,
                Getter = (ele) => (ele as QdBeamInstance).Parameters.GetValue<double>("Bottom"),
                Setter = (ele, value) =>
                {
                    var beam = (ele as QdBeamInstance);
                    if (!double.TryParse(value.ToString(),out var bottom)||bottom<0)
                        return;
                    beam.OnPropertyChangedBefore("Bottom",beam.Parameters.GetValue<double>("Bottom"),bottom);
                    beam.Parameters.SetValue("Bottom",bottom );
                    beam.ResetCache();
                    beam.OnPropertyChangedAfter("Bottom",beam.Parameters.GetValue<double>("Bottom"),bottom);
                }
            },
            };
            return list;
        }
        public override void DrawDragGrip(SKCanvas canvas)
        {
            if (_beamInstance == null)
                return;
            if (_gripName == "InsertPoint")
            {
                var offset = _position - _beamInstance.Position.ToVector2();
              //  var matrix = Matrix3.GetTranslate(offset) * _beamInstance.Matrix;
                var curves = _beamInstance.Curves?.FirstOrDefault().Curve2ds;
                var pen = this.GetDrawPen(_beamInstance);
                if (pen == LightCAD.Runtime.Constants.defaultPen)
                {
                    pen = new SKPaint { Color = SKColors.White, IsStroke = true };
                }
                DrawBeamCurves(canvas, curves, pen);
            }

        }
        #endregion

    }
    public class Beam3dAction : ComponentInstance3dAction
    {
        public override List<Object3D> Render(IComponentInstance cptIns)
        {
            try
            {
                var beam = cptIns as QdBeamInstance;
                var result = new List<Object3D>();
                var lcMats = beam.Definition.Solid3dProvider.AssignMaterialsFunc(cptIns, null);
                if (beam.Lines!=null)
                {
                    BufferGeometry geo;
                    if (beam.Lines[0] is Line2d line)
                    {
                        geo = CreateLineBeam(beam,line);
                    }
                    else 
                    {
                        var arc = beam.Lines[0] as Arc2d;
                        geo = CreateArcBeam(beam,arc);
                    }
                    var mesh = new Mesh(geo, lcMats.Select(m => RenderMaterialManager.GetRenderMaterial(m.Uuid)).ToArray());
                    mesh.position.Set(beam.Transform3d.Position.X, beam.Transform3d.Position.Y, 0);
                    result.Add(mesh);
                } 
                return result;
            }
            catch (Exception ex)
            {

            }
            return null;
        }
        private BufferGeometry CreateLineBeam(QdBeamInstance beam,Line2d line)
        {
            var surfaces = new List<Surface3d>();
            var secondWidth = beam.SecondWidth > 0 ? beam.SecondWidth : beam.Width;
            var secondHeight = beam.SecondHeight > 0 ? beam.SecondHeight : beam.Height;
            var secondBottom = beam.SecondBottom > 0 ? beam.SecondBottom : beam.Bottom;
            var normal = line.Dir.RotateAround(new Vector2(), Math.PI / 2);
            var startLP = line.Start.Clone().AddScaledVector(normal, beam.Width / 2);
            var startRP = line.Start.Clone().AddScaledVector(normal, -beam.Width / 2);
            var endLP = line.End.Clone().AddScaledVector(normal, secondWidth / 2);
            var endRP = line.End.Clone().AddScaledVector(normal, -secondWidth / 2);
            var tslp = startLP.ToVector3(beam.Bottom + beam.Height);
            var tsrp = startRP.ToVector3(beam.Bottom + beam.Height);
            var telp = endLP.ToVector3(secondBottom +secondHeight);
            var terp = endRP.ToVector3(secondBottom +secondHeight);
            var bslp = startLP.ToVector3(beam.Bottom);
            var bsrp = startRP.ToVector3(beam.Bottom);
            var belp = endLP.ToVector3(secondBottom);
            var berp = endRP.ToVector3(secondBottom);
            var topLoops = new List<Curve3d>();
            topLoops.Add(new Line3d(tslp.Clone(),telp.Clone()));
            topLoops.Add(new Line3d(telp.Clone(), terp.Clone()));
            topLoops.Add(new Line3d(terp.Clone(), tsrp.Clone()));
            topLoops.Add(new Line3d(tsrp.Clone(), tslp.Clone()));
            var topFace = new PlanarSurface3d(new Plane(telp.Clone().Sub(tslp).Cross(terp.Clone().Sub(telp)).Normalize().Negate()),topLoops);
            surfaces.Add(topFace);
            var bottomLoops = new List<Curve3d>();
            bottomLoops.Add(new Line3d(bslp.Clone(), belp.Clone()));
            bottomLoops.Add(new Line3d(belp.Clone(), berp.Clone()));
            bottomLoops.Add(new Line3d(berp.Clone(), bsrp.Clone()));
            bottomLoops.Add(new Line3d(bsrp.Clone(), bslp.Clone()));
            var bottomFace = new PlanarSurface3d(new Plane(belp.Clone().Sub(bslp).Cross(berp.Clone().Sub(belp)).Normalize()), bottomLoops);
            surfaces.Add(bottomFace);
            var startLoop = new List<Curve3d>();
            startLoop.Add(new Line3d(tslp.Clone(), bslp.Clone()));
            startLoop.Add(new Line3d(bslp.Clone(), bsrp.Clone()));
            startLoop.Add(new Line3d(bsrp.Clone(), tsrp.Clone()));
            startLoop.Add(new Line3d(tsrp.Clone(), tslp.Clone()));
            var startFace = new PlanarSurface3d(new Plane(bslp.Clone().Sub(tslp).Cross(bsrp.Clone().Sub(bslp)).Normalize()), startLoop);
            surfaces.Add(startFace);
            var endLoop = new List<Curve3d>();
            endLoop.Add(new Line3d(telp.Clone(), belp.Clone()));
            endLoop.Add(new Line3d(belp.Clone(), berp.Clone()));
            endLoop.Add(new Line3d(berp.Clone(), terp.Clone()));
            endLoop.Add(new Line3d(terp.Clone(), telp.Clone()));
            var endFace = new PlanarSurface3d(new Plane(belp.Clone().Sub(telp).Cross(berp.Clone().Sub(belp)).Normalize().Negate()), endLoop);
            surfaces.Add(endFace);
            var leftLoop = new List<Curve3d>();
            leftLoop.Add(new Line3d(tslp.Clone(), telp.Clone()));
            leftLoop.Add(new Line3d(telp.Clone(), belp.Clone()));
            leftLoop.Add(new Line3d(belp.Clone(), bslp.Clone()));
            leftLoop.Add(new Line3d(bslp.Clone(), tslp.Clone()));
            var leftFace = new PlanarSurface3d(new Plane(tslp.Clone().Sub(telp).Cross(belp.Clone().Sub(telp)).Normalize().Negate()), leftLoop);
            surfaces.Add(leftFace);
            var rightLoop = new List<Curve3d>();
            rightLoop.Add(new Line3d(tsrp.Clone(), terp.Clone()));
            rightLoop.Add(new Line3d(terp.Clone(), berp.Clone()));
            rightLoop.Add(new Line3d(berp.Clone(), bsrp.Clone()));
            rightLoop.Add(new Line3d(bsrp.Clone(), tsrp.Clone()));
            var rightFace = new PlanarSurface3d(new Plane(tsrp.Clone().Sub(terp).Cross(berp.Clone().Sub(terp)).Normalize()), rightLoop);
            surfaces.Add(rightFace);
            var posArr = new ListEx<double>();
            var idxArr = new ListEx<int>();
            int idxOffset = 0;
            for (int i = 0; i < surfaces.Count; i++)
            {
                var face = surfaces[i];
                var tuple = face.Trianglate();
                idxOffset = posArr.Length / 3;
                posArr.AddRange(tuple.Item1);
                idxArr.AddRange(tuple.Item2.Select(idx => idx + idxOffset));
            }
            GeometryData geometryData = new GeometryData();
            geometryData.Position = posArr;
            geometryData.Index = idxArr;
            var geo = geometryData.GetBufferGeometry();
            geo.name = "Beam";
            geo.computeVertexNormals();
            return geo;
        }
        private BufferGeometry CreateArcBeam(QdBeamInstance beam, Arc2d arc)
        {
           
            return null;
        }
    }
}
